/*
 * Written and copyright 2001-2003 Tobias Minich. Distributed under the GNU
 * General Public License; see the README file. This code comes with NO
 * WARRANTY.
 * 
 * AddFind.java
 * 
 * Created on 23.03.2004
 *
 */
package org.gudy.azureus2.ui.console.commands;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.OptionBuilder;
import org.gudy.azureus2.ui.console.ConsoleInput;
import org.pf.file.FileFinder;
import org.pf.text.StringUtil;

import com.aelitis.azureus.core.AzureusCoreException;

/**
 * this class allows the user to add and find torrents. when adding, you may specify an output directory when finding, it will cache the files it
 * finds into the ConsoleInput object so that they can then be added by id
 * 
 * @author tobi, fatal
 */
public class AddFind extends OptionsConsoleCommand {

    public AddFind() {
        super("add", "a");

        OptionBuilder.withArgName("outputDir");
        OptionBuilder.withLongOpt("output");
        OptionBuilder.hasArg();
        OptionBuilder.withDescription("override default download directory");
        OptionBuilder.withType(File.class);
        getOptions().addOption(OptionBuilder.create('o'));
        getOptions().addOption("r", "recurse", false, "recurse sub-directories.");
        getOptions().addOption("f", "find", false, "only find files, don't add.");
        getOptions().addOption("h", "help", false, "display help about this command");
        getOptions().addOption("l", "list", false, "list previous find results");
    }

    public String getCommandDescriptions() {
        return "add [addoptions] [.torrent path|url]\t\ta\tAdd a download from the given .torrent file path or url. Example: 'add /path/to/the.torrent' or 'add http://www.url.com/to/the.torrent'";
    }

    public void execute(String commandName, ConsoleInput ci, CommandLine commands) {
        if (commands.hasOption('l')) {
            ci.out.println("> -----");
            showAdds(ci);
            ci.out.println("> -----");
            return;
        } else if (commands.hasOption('h') || commands.getArgs().length == 0) {
            printHelp(ci.out, (String) null);
            return;
        }
        String outputDir = ".";
        if (commands.hasOption('o'))
            outputDir = commands.getOptionValue('o');
        else
            outputDir = ci.getDefaultSaveDirectory();

        File f = new File(outputDir);
        if (!f.isAbsolute()) {
            // make it relative to current directory
            try {
                outputDir = new File(".", outputDir).getCanonicalPath();
            } catch (IOException e) {
                throw new AzureusCoreException("exception occurred while converting directory: ./" + outputDir + " to its canonical path");
            }
        }
        boolean scansubdir = commands.hasOption('r');
        boolean finding = commands.hasOption('f');

        String[] whatelse = commands.getArgs();
        for (int i = 0; i < whatelse.length; i++) {
            String arg = whatelse[i];
            try {
                // firstly check if it is a valid URL
                new URL(arg);
                addRemote(ci, arg, outputDir);
            } catch (MalformedURLException e) {
                // assume that it's a local file or file id from a previous find
                addLocal(ci, arg, outputDir, scansubdir, finding);
            }
        }
    }

    /**
     * attempt to download the torrent specified by 'arg' and save the files in the torrent to the specified output directory
     * 
     * @param ci
     * @param arg
     *            URL of torrent to download
     * @param outputDir
     *            directory to save files from torrent to
     */
    protected void addRemote(ConsoleInput ci, String arg, String outputDir) {
        ci.out.println("> Starting Download of " + arg + " ...");
        try {
            ci.downloadRemoteTorrent(arg, outputDir);
        } catch (Exception e) {
            ci.out.println("An error occurred while downloading torrent: " + e.getMessage());
            e.printStackTrace(ci.out);
        }
    }

    /**
     * attempt a local add (arg may be a directory, a file or a pattern eg: d:/*.torrent)
     * 
     * @param ci
     * @param arg
     *            argument - could be directory, file or pattern eg: d:\*.torrent
     * @param outputDir
     *            directory to save files from torrent to
     * @param scansubdir
     *            if true, will recurse subdirectories looking for files to add
     * @param finding
     *            if true, don't start downloading the files; simply add them to the 'found' list
     */
    protected void addLocal(ConsoleInput ci, String arg, String outputDir, boolean scansubdir, boolean finding) {
        // substitute ~ for home directory, if specified
        arg = transformLocalArgument(arg);
        // see if the argument is an existing file or directory
        File test = new File(arg);
        if (test.exists()) {
            if (test.isDirectory()) {
                File[] toadd = FileFinder.findFiles(arg, "*.torrent;*.tor", scansubdir);
                if ((toadd != null) && (toadd.length > 0)) {
                    addFiles(ci, toadd, finding, outputDir);
                } else {
                    ci.adds = null;
                    ci.out.println("> Directory '" + arg + "' seems to contain no torrent files.");
                }
            } else {
                ci.downloadTorrent(arg, outputDir);
                ci.out.println("> '" + arg + "' added.");
                ci.torrents.clear();
            }
            return;
        }

        // check to see if they are numeric and if so, try and add them from the 'adds' in ci
        try {
            int id = Integer.parseInt(arg);
            if (ci.adds != null && ci.adds.length > id) {
                String torrentPath = ci.adds[id].getAbsolutePath();
                ci.downloadTorrent(torrentPath, outputDir);
                ci.out.println("> '" + torrentPath + "' added.");
                ci.torrents.clear();
            } else {
                ci.out.println("> No such file id '" + id + "'. Try \"add -l\" to list available files");
            }
            return;
        } catch (NumberFormatException e) {
        }
        // last resort - try to process it as a directory/pattern eg: c:/torrents/*.torrent
        String dirName = test.getParent();
        if (dirName == null)
            dirName = ".";
        String filePattern = test.getName();
        File[] files = FileFinder.findFiles(dirName, filePattern, false);
        if ((files != null) && (files.length > 0)) {
            addFiles(ci, files, finding, outputDir);
        } else {
            ci.adds = null;
            ci.out.println("> No files found. Searched for '" + filePattern + "' in '" + dirName + "'");
        }
    }

    /**
     * perform any transformations on the argument - in this case we are replacing '~' with the user's home directory.
     * 
     * @param arg
     * @return
     */
    protected String transformLocalArgument(String arg) {
        if (arg.startsWith("~/") || arg.equals("~")) {
            arg = StringUtil.current().replaceAll(arg, "~", System.getProperty("user.home"));
        }
        return arg;
    }

    /**
     * if finding is set, just print the available files and add them to the 'add' list inside the consoleinput object, otherwise actually add the
     * torrents, saving to the specified output directory
     * 
     * @param toadd
     * @param finding
     * @param outputDir
     */
    protected void addFiles(ConsoleInput ci, File[] toadd, boolean finding, String outputDir) {
        ci.out.println("> -----");
        ci.out.println("> Found " + toadd.length + " files:");

        if (finding) {
            ci.adds = toadd;
            showAdds(ci);
        } else {
            for (int i = 0; i < toadd.length; i++) {
                ci.downloadTorrent(toadd[i].getAbsolutePath(), outputDir);
                ci.out.println("> '" + toadd[i].getAbsolutePath() + "' added.");
                ci.torrents.clear();
            }
        }
        ci.out.println("> -----");
    }

    /**
     * prints out the files in the 'add' list that is stored in the console input object.
     * 
     * @param ci
     */
    private void showAdds(ConsoleInput ci) {
        if (ci.adds == null || ci.adds.length == 0) {
            ci.out.println("> No files found. Try \"add -f <path>\" first");
            return;
        }
        for (int i = 0; i < ci.adds.length; i++) {
            ci.out.print(">\t" + i + ":\t");
            try {
                ci.out.println(ci.adds[i].getCanonicalPath());
            } catch (Exception e) {
                ci.out.println(ci.adds[i].getAbsolutePath());
            }
        }
        ci.out.println("> To add, simply type 'add <id>'");
    }
}
